﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GameEngine.MoveHistory
{
    /// <summary>
    /// Old linear move history model supporting only one line of game
    /// </summary>
    internal class LinearModeHistoryModel: IMoveHistoryModel
    {
        /// <summary>
        /// Two stacks for keeping move history and actual position
        /// </summary>
        Stack<Move> m_MovesDone;
        Stack<Move> m_MovesToDo;

        public GameBoard GameBoard
        { get; set; }

        public LinearModeHistoryModel()
        {
            m_MovesDone = new Stack<Move>();
            m_MovesToDo = new Stack<Move>();
        }

        /// <summary>
        /// Returns all moves done
        /// </summary>
        /// <param name="list"></param>
        public void GetMoveList(List<Move> list)
        {
            list.AddRange(m_MovesDone);
            list.AddRange(m_MovesToDo);
        }

        /// <summary>
        /// Returns moves done
        /// </summary>
        /// <param name="list"></param>
        public void GetMovesDone(List<Move> list)
        {
            list.AddRange(m_MovesDone);
            list.Reverse();
        }

        /// <summary>
        /// Returns moves to do
        /// </summary>
        /// <param name="list"></param>
        public void GetMovesToDo(List<Move> list)
        {
            list.AddRange(m_MovesToDo);
        }

        /// <summary>
        /// Makes game history empty, e.g. for starting board editor
        /// </summary>
        public void ClearMoves()
        {
            m_MovesDone.Clear();
            m_MovesToDo.Clear();
        }

        /// <summary>
        /// Clears moves to do
        /// </summary>
        public void ClearMovesToDo()
        {
            m_MovesToDo.Clear();
        }

        /// <summary>
        /// Adds a new move to the end
        /// </summary>
        public void AddMove(Move move)
        {
            AddMoveToStack(move);
        }

        /// <summary>
        /// Returns the number of moves done
        /// </summary>
        /// <returns></returns>
        public int GetMovesDoneCount()
        {
            return m_MovesDone.Count;
        }

        /// <summary>
        /// Returns the current index of move
        /// </summary>
        /// <returns></returns>
        public int GetMoveIndex()
        {
            return m_MovesDone.Count;
        }

        /// <summary>
        /// Moves to the move with specific game index
        /// </summary>
        /// <param name="idx"></param>
        public void MoveToIndex(int idx)
        {
            if (idx == m_MovesDone.Count)
                return;
            else if (idx > m_MovesDone.Count)
            {
                while (m_MovesToDo.Count != 0 && m_MovesDone.Count != idx)
                    MoveForward();
            }
            else
            {
                while (m_MovesDone.Count != 0 && m_MovesDone.Count != idx)
                    MoveBack();
            }
        }

        /// <summary>
        /// Are we able to move forward?
        /// </summary>
        /// <returns></returns>
        public bool CanMoveForward()
        {
            return m_MovesToDo.Count != 0;
        }

        /// <summary>
        /// Are we able to move back
        /// </summary>
        /// <returns></returns>
        public bool CanMoveBack()
        {
            return m_MovesDone.Count != 0;
        }


        /// <summary>
        /// Move back in the game
        /// </summary>
        public void MoveBack()
        {
            Move move = MoveBackInStack();
            if (move != null)
            {
                GameBoard.UndoMove(move);
            }
        }

        /// <summary>
        /// Move forward in the game
        /// </summary>
        public void MoveForward()
        {
            Move move = MoveForwardInStack();
            if (move != null)
            {
                GameBoard.DoMove(move);
                GameBoard.ChangeOnMove();
            }
        }


        #region Helper methods to work with stack

        /// <summary>
        /// Helper method for working with stack: move back
        /// </summary>
        /// <returns></returns>
        private Move MoveBackInStack()
        {
            if (CanMoveBack() == false)
                return null;
            Move move = m_MovesDone.Pop();
            m_MovesToDo.Push(move);

            return move;
        }

        /// <summary>
        /// Helper method for working with move stack: move forward
        /// </summary>
        /// <returns></returns>
        private Move MoveForwardInStack()
        {
            if (CanMoveForward() == false)
                return null;
            Move move = m_MovesToDo.Pop();
            m_MovesDone.Push(move);

            return move;
        }

        /// <summary>
        /// Helper method to work with stack, add move to stack
        /// </summary>
        /// <param name="move"></param>
        private void AddMoveToStack(Move move)
        {
            m_MovesDone.Push(move);
            m_MovesToDo.Clear();
        }

        #endregion
    }
}
